package goshell

import (
	"encoding/json"
	"gitee.com/zhouhailin/gopkg/gofile"
	"gitee.com/zhouhailin/gopkg/gopath"
	"gitee.com/zhouhailin/gopkg/gostrings"
	"gitee.com/zhouhailin/gopkg/gotime"
	"github.com/bytedance/gopkg/util/logger"
	"io"
	"os/exec"
	"strings"
)

// Command 命令
type Command struct {
	Dir string
	Cmd string
}

// map2string map转string
func map2string(m map[string]string) string {
	data, err := json.Marshal(m)
	if err != nil {
		return ""
	}
	return string(data)
}

// writeFile 写文件
func writeFile(cmd *Command) (string, error) {
	var sb strings.Builder
	sb.WriteString("#/bin/bash" + gostrings.LF + gostrings.LF)
	sb.WriteString("export LANG=en_US.UTF-8" + gostrings.LF + gostrings.LF)
	if cmd.Dir != "" {
		sb.WriteString("cd " + cmd.Dir + gostrings.LF + gostrings.LF)
	} else {
		sb.WriteString("cd " + gopath.GetWorkDir() + gostrings.LF + gostrings.LF)
	}
	sb.WriteString(cmd.Cmd)
	pathname := gopath.GetTempDir() + "/" + gotime.NowMillisStr() + ".sh"
	err := gofile.WriteText(pathname, sb.String())
	if err != nil {
		return gostrings.EMPTY, err
	}
	return pathname, nil
}

// executeShellFile 执行bash文件
func executeShellFile(pathname string) string {
	m := map[string]string{}
	m["startTime"] = gotime.Now()
	m["stderr"] = gostrings.EMPTY
	m["stdout"] = gostrings.EMPTY

	cmd := exec.Command("/bin/sh", "-c", pathname)
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		m["stderr"] = err.Error()
		m["endTime"] = gotime.Now()
		return map2string(m)
	}
	stderr, err := cmd.StderrPipe()
	if err != nil {
		m["stderr"] = err.Error()
		m["endTime"] = gotime.Now()
		return map2string(m)
	}
	if err := cmd.Start(); err != nil {
		m["stderr"] = err.Error()
		m["endTime"] = gotime.Now()
		return map2string(m)
	}

	bytesErr, err := io.ReadAll(stderr)
	if err != nil {
		m["stderr"] = err.Error()
		m["endTime"] = gotime.Now()
		return map2string(m)
	}

	if len(bytesErr) != 0 {
		m["stderr"] = string(bytesErr)
		m["endTime"] = gotime.Now()
		return map2string(m)
	}

	bytes, err := io.ReadAll(stdout)
	if err != nil {
		m["stderr"] = err.Error()
		//fmt.Println("ReadAll stdout: ", err.Error())
		m["endTime"] = gotime.Now()
		return map2string(m)
	}

	if err := cmd.Wait(); err != nil {
		m["stderr"] = err.Error()
		//fmt.Println("Wait: ", err.Error())
		m["endTime"] = gotime.Now()
		return map2string(m)
	}
	m["stdout"] = string(bytes)
	m["endTime"] = gotime.Now()
	data, err := json.Marshal(m)
	if err != nil {
		return map2string(m)
	}
	return string(data)
}

func deleteFile(filename string) error {
	return gofile.DeleteFile(filename)
}

func executeCommand(cmd *Command) (string, error) {
	filename, err := writeFile(cmd)
	if err != nil {
		return gostrings.EMPTY, err
	}
	defer func(filename string) {
		err := deleteFile(filename)
		if err != nil {
			logger.Errorf("delete file failure, err : %v", err)
		}
	}(filename)
	resp := executeShellFile(filename)
	return resp, nil
}

// ExecuteSingleLineCommand 执行单行命令
func ExecuteSingleLineCommand(line string) (string, error) {
	return executeCommand(&Command{Cmd: line})
}

// ExecuteSingleLineCommandWithWork 在指定工作目录下执行单行命令
func ExecuteSingleLineCommandWithWork(line string, work string) (string, error) {
	return executeCommand(&Command{Cmd: line, Dir: work})
}

// ExecuteMultiLinesCommand 执行多行命令
func ExecuteMultiLinesCommand(lines []string) (string, error) {
	return executeCommand(&Command{Cmd: gostrings.MultiLinesToLines(lines)})
}

// ExecuteMultiLinesCommandWithWork 在指定工作目录下执行多行命令
func ExecuteMultiLinesCommandWithWork(lines []string, work string) (string, error) {
	return executeCommand(&Command{Cmd: gostrings.MultiLinesToLines(lines), Dir: work})
}
